#include <QUndoStack>

#include <gp_Pnt.hxx>
#include <gp_Circ.hxx>
#include <Geom_BSplineCurve.hxx>
#include <Geom_TrimmedCurve.hxx>
#include <BRepLib_MakeWire.hxx>
#include <TopoDS_Edge.hxx>
#include <TopoDS_Face.hxx>
#include <BRepLib_MakeEdge.hxx>
#include <TopoDS_Wire.hxx>
#include <BRepFill_Generator.hxx>
#include <TopExp_Explorer.hxx>
#include <TopoDS.hxx>
#include <BRep_Builder.hxx>

#include "RuledSurfaceHandler.h"
#include "Qt3DPicker.h"

#include "CmdManager.h"
#include "Scene.h"
#include "SendPointHandlerBase.h"
#include "SelectHandler3D.h"
#include "Operation/OpAdd.h"
#include "LineStrip3D.h"
#include "Arc3D.h"
#include "Spline3D.h"
#include "Surface.h"
#include "QtOSGWidget.h"

RuledSurfaceHandler::RuledSurfaceHandler()
{
}

RuledSurfaceHandler::~RuledSurfaceHandler()
{
}

void RuledSurfaceHandler::input(const QVariant& data, const QString& rawInput, CommandSource cs)
{
	auto scene = getScene();
	auto widget3D = getWidget();
	auto sph = getSendPointHandler();
	auto selector = qobject_cast<SelectHandler3D*>(getSelectHandler());
	if (data.type() == QVariant::Bool)
	{
		bool esc = !data.toBool();
		if (esc)
		{
			selector->clearSelection();
			clearInput();
			if (m_curve1)
			{
				m_curve1->setSelected(false);
			}
			if (m_surface)
			{
				auto parent = m_surface->getParent();
				if (parent)
				{
					widget3D->pauseRenderThread();
					parent->removeChild(m_surface);
					widget3D->resumeRenderThread();
				}
			}
			emit finished();
			return;
		}
	}

	if (status() == 0)
	{
		selector->beginSelect(D3CurveMask, 1);
		prompt(1, tr("选择三维曲线: "));
		return;
	}

	if (status() == 1)
	{
		if (selector->done())
		{
			std::set<std::shared_ptr<INode>> nodes = selector->selectionSet();
			if (nodes.empty())
			{
				emit finished();
				return;
			}
			std::shared_ptr<INode> node = *nodes.begin();
			auto curve1 = std::dynamic_pointer_cast<Ent>(node);
			if (curve1)
			{
				m_curve1 = curve1;
				selector->clearSelection();
				m_curve1->setSelected(true);
				selector->beginSelect(D3CurveMask, 1);
				prompt(2, tr("选择第二条三维曲线: "));
			}
		}
		return;
	}

	if (status() == 2)
	{
		if (selector->done())
		{
			std::set<std::shared_ptr<INode>> nodes = selector->selectionSet();
			if (nodes.empty())
			{
				emit finished();
				return;
			}
			std::shared_ptr<INode> node = *nodes.begin();
			auto curve2 = std::dynamic_pointer_cast<Ent>(node);
			if (curve2)
			{
				m_curve2 = curve2;
				m_surface = makeRuledSurface(false);
				if (m_surface)
				{
					widget3D->pauseRenderThread();
					scene->addChild(m_surface);
					widget3D->resumeRenderThread();
					prompt(3, tr("反转第二曲线方向[是(Y)/否(N)]<N>: "));
				}
				else
				{
					remark(tr("创建直纹曲面失败"));
					emit finished();
				}
				selector->clearSelection();
				m_curve1->setSelected(false);
			}
		}
		return;
	}

	if (status() == 3)
	{
		bool reverse = false;
		widget3D->pauseRenderThread();
		scene->removeChild(m_surface);
		widget3D->resumeRenderThread();
		if (data.type() == QVariant::String)
		{
			auto command = data.value<QString>();
			if (command == tr("Y") || command == tr("y"))
			{
				reverse = true;
			}
		}
		if (reverse)
		{
			m_surface = makeRuledSurface(reverse);
		}
		auto undoStack = scene->undoStack();
		std::set<std::shared_ptr<INode>> children;
		children.insert(m_surface);
		undoStack->push(new OpAdd(children));
		emit finished();
	}
}

TopoDS_Wire lineToWire(const LineStrip3D& line, bool reverse)
{
	const auto& points = line.getPoints();
	if (points.size() != 2)
		return TopoDS_Wire();
	BRepLib_MakeEdge emaker(gp_Pnt(points[0].x(), points[0].y(), points[0].z()), gp_Pnt(points[1].x(), points[1].y(), points[1].z()));
	auto edge = emaker.Edge();
	if (reverse)
		edge.Reverse();
	BRepLib_MakeWire wmaker;
	wmaker.Add(edge);
	return wmaker.Wire();
}

TopoDS_Wire arcToWire(const Arc3D& arc, bool reverse)
{
	auto centerWCS = arc.center() * arc.getMatrix();
	gp_Pnt origin(centerWCS.x(), centerWCS.y(), centerWCS.z());
	auto zA = arc.zAxis();
	gp_Dir zDir(zA.x(), zA.y(), zA.z());
	auto xA = arc.xAxis();
	gp_Dir xDir(xA.x(), xA.y(), xA.z());
	gp_Ax2 axis(origin, zDir, xDir);
	gp_Circ circ(axis, arc.radius());
	BRepLib_MakeEdge emaker(circ, arc.startAngle(), arc.endAngle());
	auto edge = emaker.Edge();
	if (reverse)
		edge.Reverse();
	BRepLib_MakeWire wmaker;
	wmaker.Add(edge);
	return wmaker.Wire();
}

Handle(Geom_BSplineCurve) getCurve(int m_Flag,
	int m_Degree,
	const std::vector<double>& m_Knots,
	std::vector<double>& m_Weights,
	const std::vector<osg::Vec3d>& m_ControlPoints
);
TopoDS_Wire splineToWire(const Spline3D& spline, bool reverse)
{
	auto weights = spline.getWeights();
	auto bspline = getCurve(spline.getFlag(), spline.getDegree(), spline.getKnots(), weights, spline.getCtrlPoints());
	if (reverse)
		bspline->Reverse();
	BRepLib_MakeEdge emaker;
	emaker.Init(bspline);
	BRepLib_MakeWire wmaker;
	wmaker.Add(emaker.Edge());
	return wmaker.Wire();
}

TopoDS_Wire curveToWire(const std::shared_ptr<Ent>& ent, bool reverse)
{
	auto line = std::dynamic_pointer_cast<LineStrip3D>(ent);
	if (line)
	{
		return lineToWire(*line, reverse);
	}
	auto arc = std::dynamic_pointer_cast<Arc3D>(ent);
	if (arc)
	{
		return arcToWire(*arc, reverse);
	}
	auto spline = std::dynamic_pointer_cast<Spline3D>(ent);
	if (spline)
	{
		return splineToWire(*spline, reverse);
	}
	return TopoDS_Wire();
}

TopoDS_Wire makeWireFromPoint(const osg::Vec3d& pt)
{
	BRep_Builder BB;
	TopoDS_Vertex aVertex;
	BB.MakeVertex(aVertex, gp_Pnt(pt.x(), pt.y(), pt.z()), 0.001);
	TopoDS_Edge DegEdge;
	BB.MakeEdge(DegEdge);
	BB.Add(DegEdge, aVertex.Oriented(TopAbs_FORWARD));
	BB.Add(DegEdge, aVertex.Oriented(TopAbs_REVERSED));
	BB.Degenerated(DegEdge, Standard_True);

	TopoDS_Wire DegWire;
	BB.MakeWire(DegWire);
	BB.Add(DegWire, DegEdge);
	DegWire.Closed(Standard_True);
	return DegWire;
}

std::shared_ptr<Surface> RuledSurfaceHandler::makeRuledSurface(bool reverseCurve2)
{
	TopoDS_Wire wire1 = curveToWire(m_curve1, false);
	TopoDS_Wire wire2;
	if (m_curve2)
		wire2 = curveToWire(m_curve2, reverseCurve2);
	else
		wire2 = makeWireFromPoint(m_point);
	if (wire1.IsNull() || wire2.IsNull())
		return std::shared_ptr<Surface>();
	BRepFill_Generator rsg;
	rsg.AddWire(wire1);
	rsg.AddWire(wire2);
	rsg.Perform();
	TopoDS_Shell shell = rsg.Shell();
	TopExp_Explorer Ex;
	for (Ex.Init(shell, TopAbs_FACE); Ex.More(); Ex.Next())
	{
		const TopoDS_Face& face = TopoDS::Face(Ex.Current());
		if (!face.IsNull())
		{
			auto scene = getScene();
			auto surface = std::make_shared<Surface>(scene);
			surface->setShape(face);
			return surface;
		}
	}
	return std::shared_ptr<Surface>();
}
